package com.personal.dataanalyse.reportmanage.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.personal.core.bean.TwoTuple;
import com.personal.core.data.DataColumn;
import com.personal.core.data.DataRow;
import com.personal.core.data.DataTable;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.DeepChildrenUtil;
import com.personal.dataanalyse.consts.FieldConsts;
import com.personal.dataanalyse.enums.AdditionalTypeEnum;
import com.personal.dataanalyse.enums.FieldDataTypeEnum;
import com.personal.dataanalyse.enums.ModelFieldTypeEnum;
import com.personal.dataanalyse.reportmanage.base.DataAnalyseModel;
import com.personal.dataanalyse.reportmanage.base.DataChartModel;
import com.personal.dataanalyse.reportmanage.base.QuotaInfo;
import com.personal.dataanalyse.reportmanage.entity.ConditionInfo;
import com.personal.dataanalyse.reportmanage.entity.DataColumnReportEx;
import com.personal.dataanalyse.reportmanage.entity.DataRowReportEx;
import com.personal.dataanalyse.reportmanage.entity.ReportHeader;
import com.personal.dataanalyse.reportmanage.util.ModelUtil;
import com.personal.dataconvert.DataTable2Html;
import com.personal.dataconvert.ExportData2Html;
import com.personal.dataconvert.bean.HeaderConfig;
import com.personal.dataconvert.port.Data2Html;
import com.personal.dataconvert.util.ExcelHtmlUtil;


/**
 * 报表模型
 * @author cuibo
 *
 */
public class ReportModel extends DataAnalyseModel
{
    /**
     * 
     */
    private static final long serialVersionUID = 6672389258779976112L;
    
    /** 报表的装饰 */
    protected ReportDecorateModel decorateModel;
    
    public ReportModel()
    {
        super();
        this.decorateModel = new ReportDecorateModel();
    }
    
    @Override
    public DataTable toDataTable() throws Exception
    {
        DataTable result = super.toDataTable();
        // 移除不匹配的行列
        if (decorateModel.isRemoveNoMatchRowCol())
        {
            removeRowCol(result, REMOVETYPE_NOMATCH);
        }
        return result;
    }
    
    /**
     * 展示报表结构
     * 在toDataTableOnlyStructImpl的基础针对XTotalModel移除了第一个指标值列
     * 在父类的基础上添加合计行
     * @return
     * @throws Exception
     */
    @Override
    public DataTable toDataTableOnlyStruct() throws Exception
    {
        DataTable result = super.toDataTableOnlyStruct(); 
        // 添加附加行
        if (!isTransRowCol())
        {
            for (AdditionalTypeEnum additionalType : AdditionalTypeEnum.values())
            {
                String displayName = decorateModel.getOppositeProperty(additionalType);
                if (CoreUtil.isEmpty(displayName))
                {
                    continue;
                }
                addAdditionalRow(result, additionalType, displayName, true);
            }
        }
        return result;
    }
    
    @Override
    protected void handelXSingleGroupTotalModel(DataTable result)
    {
        // 如果是 XTotalModel需要移除第一个指标值列:有附加行又不能移除
        if (isXSingleGroupTotalModel() && result.getColumns() != null && result.getColumns().size() > 0
                && !decorateModel.hasAdditionalRowCol())
        {
            result.getColumns().remove(0);
            // 移除对应的第一个HeaderConfig
            headerConfigs.remove(0);
        }
    }

    @Override
    protected DataTable toDataTableImpl() throws Exception
    {
        DataTable result = super.toDataTableImpl();
        // 装饰数据
        decorateResult(result);
        return result;
    }
    
    @Override
    protected DataTable toDataTableOnlyStructImpl() throws Exception
    {
        DataTable result = super.toDataTableOnlyStructImpl();
        // 装饰数据
        if (result != null)
        {
            result.setLeftHeader(decorateModel.getLeftHeader());
            result.setRightHeader(decorateModel.getRightHeader());
        }
        // 添加附加列
        if (isTransRowCol())
        {
            for (AdditionalTypeEnum additionalType : AdditionalTypeEnum.values())
            {
                String displayName = decorateModel.getOppositeProperty(additionalType);
                if (CoreUtil.isEmpty(displayName))
                {
                    continue;
                }
                addAdditionalCol(result, additionalType, displayName);
            }
        }
        if (decorateModel.isHeaderAddUnit())
        {
            // 表头加上单位
            ModelUtil.addHeaderConfigUnit(headerConfigs);
            // 重新替换DataColumn的columnLabel
            List<ReportHeader> leafs = DeepChildrenUtil.getLeafs(headerConfigs);
            for (int i = 0; i < leafs.size(); i++)
            {
                result.getColumns().get(i).setColumnLable(appendHeaderPath(leafs.get(i)));
            }
            // 如果是行列转换model，需要给指标列的行数据加上单位
            if (isTransRowCol())
            {
                ModelUtil.addTargetColUnit(result);
            }
        }
        return result;
    }
    
    /**
     * 装饰结果
     * @param result
     */
    private void decorateResult(DataTable result)
    {
        // 没有数据忽略所有装饰操作
        if (!CoreUtil.checkDataTableHasData(result)) 
        {
            return;
        }
        // 装饰数据
        boolean isTransRowCol = this.isTransRowCol();
        // 行列转换的model不需要做任何处理,因为都是转成正常的model，然后行列转换填充的
        // 也就是行列转换的model根本就不会走该方法
        if (isTransRowCol)
        {
            return;
        }
        // stepOne:添加附加行
        for (AdditionalTypeEnum additionalType : AdditionalTypeEnum.values())
        {
            String displayName = decorateModel.getOppositeProperty(additionalType);
            if (CoreUtil.isEmpty(displayName))
            {
                continue;
            }
            addAdditionalRow(result, additionalType, displayName, false);
        }
        // stepTwo:设置小数位（行列转换表格的小数位是在行上面设置的）
        ModelUtil.setDotNum(result, isTransRowCol);
    }

    /**
     * 往上递归获取header的最新路径
     * @param header
     * @return
     */
    private String appendHeaderPath(HeaderConfig header)
    {
        List<HeaderConfig> configs = new ArrayList<HeaderConfig>();
        configs.add(header);
        while (header.getParent() != null)
        {
            configs.add(header.getParent());
            header = header.getParent();
        }
        int size = configs.size() - 1;
        StringBuilder result = new StringBuilder();
        for (int i = size; i > -1; i--)
        {
            HeaderConfig headerConfig = configs.get(i);
            result.append(headerConfig.getDisplayName()).append(ExcelHtmlUtil.REPLACEPOINTFLAG);
        }
        return result.substring(0, result.length() - ExcelHtmlUtil.REPLACEPOINTFLAG.length());
    }

    /**
     * 添加附加列
     * @param struct
     * @param additionalType
     * @param displayName
     */
    private void addAdditionalCol(DataTable struct, AdditionalTypeEnum additionalType, String displayName) throws Exception
    {
        int colNum = struct.getColumns().size();
        // 给里面的row初始化key
        String shortUuid = CoreUtil.newShortUuId();
        struct.addNewColumn(shortUuid);
        
        // 给附加列添加数据的样式
        if (struct.getRows() != null && !struct.getRows().isEmpty())
        {
            StringBuilder builder = new StringBuilder();
            for (DataRow row : struct.getRows())
            {
                builder.setLength(0);
                DataRowReportEx rowEx = (DataRowReportEx)row;
                builder.append(FieldConsts.DATA_CLASS).append(FieldConsts.STR_UNDERLINE).append(rowEx.getRowNum())
                    .append(FieldConsts.STR_UNDERLINE).append(colNum);
                row.getItemMap().put(shortUuid + ExportData2Html.TDCLASS, builder.toString());
            }
        }
        
        // 然后在移除该column,使用此操作就是为了初始化key
        struct.getColumns().remove(struct.getColumns().size() - 1);
        DataColumnReportEx columnEx = new DataColumnReportEx(displayName);
        // 合计列默认是匹配上的列
        columnEx.setMatchData(true);
        columnEx.setDisplay(true);
        // columnName 换成UUID
        columnEx.setColumnName(shortUuid);
        // 附加类型的值一定是数值
        columnEx.setDataType(FieldDataTypeEnum.数值.toString());
        // 样式信息和前面的数据保持一致
        DataColumn styleColumn = struct.getColumns().get(struct.getColumns().size() - 1);
        columnEx.setTag(styleColumn.getTag());
        columnEx.setDataType(styleColumn.getDataType());
        columnEx.setAlign(styleColumn.getAlign());
        columnEx.setWidth(styleColumn.getWidth());
        columnEx.setAdditionalType(additionalType);
        columnEx.setColNum(colNum);
        
        // 设置conditions用于图表匹配
        List<ConditionInfo> conditionInfos = new ArrayList<ConditionInfo>();
        ConditionInfo conditionInfo = new ConditionInfo(null, FieldDataTypeEnum.数值.toString(), null, null, false, displayName, null);
        conditionInfo.setDimensionDisplayName(displayName);
        Collections.addAll(conditionInfos, conditionInfo);

        columnEx.setConditions(conditionInfos);
        
        struct.getColumns().add(columnEx);
        
        // 添加对应的表头配置
        if (headerConfigs != null)
        {
            headerConfigs.add(columnEx.toHeaderConfig());
        }
    }
    
    /**
     * 添加附件行（合计，均值，最值）
     * 只有不是行列转换的model才会添加合计行
     * @param result
     * @param additionalType  附加行类型
     * @param displayName   附加行名称
     * @param isBuildStruct  是否是构建结构
     */
    protected void addAdditionalRow(DataTable result, AdditionalTypeEnum additionalType, String displayName, boolean isBuildStruct)
    {
        DataRowReportEx newRow = new DataRowReportEx();
        // 附加行默认是匹配上的行
        newRow.setMatchData(true);
        newRow.setDisplay(true);
        newRow.setRowNum(result.getRows().size());
        newRow.initItemMap(result);
        newRow.setAdditionalType(additionalType);
        // 附加行需要加一个样式
        newRow.getItemMap().put(Data2Html.TRCLASS, FieldConsts.ADDITIONAL_ROW_COL + "_" + additionalType.toString());
        result.getRows().add(newRow);
        // 设置conditions用于图表匹配
        List<ConditionInfo> conditionInfos = new ArrayList<ConditionInfo>();
        ConditionInfo conditionInfo = new ConditionInfo(null, null, null, null, false, displayName, null);
        conditionInfo.setDimensionDisplayName(displayName);
        Collections.addAll(conditionInfos, conditionInfo);
        newRow.setConditions(conditionInfos);
        // 填充合计行数据
        fillAdditionalRow(newRow, result, additionalType, displayName, isBuildStruct);
        // 处理时计算列的问题:只是构建结构时不需要考虑填充数据
        // 最后处理计算列
        if (!isBuildStruct)
        {
            handleAdditionalRowCalCol(newRow, result);
        }
    }

    /**
     * 添加合计行处理计算列
     * @param newRow
     * @param result
     */
    private void handleAdditionalRowCalCol(DataRowReportEx newRow, DataTable result)
    {
        DataColumnReportEx columnEx = null;
        for (DataColumn dataColumn : result.getColumns())
        {
            if (!(dataColumn instanceof DataColumnReportEx))
            {
                continue;
            }
            columnEx = (DataColumnReportEx) dataColumn;
            if (ModelFieldTypeEnum.计算列.toString().equals(columnEx.getFieldType()))
            {
                newRow.setValue(dataColumn, CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), newRow.getItemMap()));
            }
        }
        
    }

    /**
     * 填充附加行数据
     * @param newRow
     * @param result
     * @param additionalType  附加行类型
     * @param displayName  附加行显示名
     * @param isBuildStruct  是否是构建结构
     */
    private void fillAdditionalRow(DataRowReportEx newRow, DataTable result, AdditionalTypeEnum additionalType, String displayName, boolean isBuildStruct)
    {
        // 把是otherGroupCol的列值都设置为合计，然后横向合并
        DataColumnReportEx columnEx = null;
        // 拿取所有行的所有单个指标汇总
        // key：columnName + 单个指标名 + 求和.toString()
        TwoTuple<Integer, Map<String, Object>> twoTuple = loadAllRowCacheMap(result);
        
        int colIndex = -1;
        int maxColIndex = -1;
        for (DataColumn dataColumn : result.getColumns())
        {
            colIndex ++;
            if (!(dataColumn instanceof DataColumnReportEx))
            {
                continue;
            }
            columnEx = (DataColumnReportEx) dataColumn;
            // 另外一个维度的名称列设置为合计值
            if (columnEx.isOtherGroupCol())
            {
                maxColIndex = colIndex;
                newRow.getItemMap().put(columnEx.getColumnName() + DataTable2Html.TDCLASS, FieldConsts.HEAD_CLASS);
                newRow.setValue(columnEx, displayName);
                continue;
            }
            // 构建结构时不考虑数据
            if (isBuildStruct)
            {
                continue;
            }
            // 有匹配的非附加行才需要计算出添加的附加行的值
            if (FieldDataTypeEnum.数值.toString().equals(columnEx.getDataType()) && twoTuple.getA() > 0)
            {
                fillAdditionalRowImpl(result, additionalType, columnEx, newRow, twoTuple);
            }
        }
        // 没有Y轴维度的情况下，把第一个字符串列设置为合计
        if (maxColIndex == -1)
        {
            columnEx = (DataColumnReportEx) result.getColumns().get(0);
            if (FieldDataTypeEnum.字符串.toString().equals(columnEx.getDataType()))
            {
                newRow.getItemMap().put(columnEx.getColumnName() + DataTable2Html.TDCLASS, FieldConsts.HEAD_CLASS);
                newRow.setValue(columnEx, displayName);
            }
        }
    }
    
    protected void fillAdditionalRowImpl(DataTable result, AdditionalTypeEnum additionalType, DataColumnReportEx columnEx,
            DataRowReportEx newRow, TwoTuple<Integer, Map<String, Object>> twoTuple)
    {
        AdditionalTypeEnum.fillAdditionalRow(result, additionalType, columnEx, newRow, twoTuple);
    }
    
    /**
     * key：columnName + 单个指标名 + 求和.toString()
     * @param table
     * @return A:行的总个数(非附加行的个数，附加行不参与非附加行的计算)   B：每行的缓存Map加起来的
     */
    @SuppressWarnings("unchecked")
    private TwoTuple<Integer, Map<String, Object>> loadAllRowCacheMap(DataTable table)
    {
        int matchRowCount = 0;
        Map<String, Object> map = new HashMap<String, Object>();
        // 单条的缓存
        Map<String, Object> singleCache = null;
        for (DataRow row : table.getRows())
        {
            DataRowReportEx rowReportEx = (DataRowReportEx)row;
            // 附加行不参与附加行的计算
            if (rowReportEx.isAdditionalRow())
            {
                continue;
            }
            matchRowCount ++;
            singleCache = (Map<String, Object>) row.getItemMap().get(FieldConsts.STR_CACHE);
            if (singleCache == null || singleCache.isEmpty())
            {
                continue;
            }
            for (Entry<String, Object> entry : singleCache.entrySet())
            {
                map.put(entry.getKey(), CoreUtil.addWithNull(map.get(entry.getKey()), entry.getValue()));
            }
        }
        return new TwoTuple<Integer, Map<String,Object>>(matchRowCount, map);
    }

    /**
     * 创建指标值列
     * @return
     */
    @Override
    public QuotaInfo createTargetValueQuotaInfo()
    {
        QuotaInfo quotaInfo = new ReportQuota();
        quotaInfo.setModel(this);
        quotaInfo.createTargetValueQuotaInfo();
        return quotaInfo;
    }

    public List<DataChartModel> getChartModels()
    {
        return chartModels;
    }

    public void setChartModels(List<DataChartModel> chartModels)
    {
        this.chartModels = chartModels;
    }

    public ReportDecorateModel getDecorateModel()
    {
        return decorateModel;
    }

    public void setDecorateModel(ReportDecorateModel decorateModel)
    {
        this.decorateModel = decorateModel;
    }
    
    @Override
    protected DataTable getDataByDataSource(Set<String> queryTargetNames) throws Exception
    {
        // TODO Auto-generated method stub
        return null;
    }
    
}